package com.suzus.aiService.service.impl;

import com.baidubce.qianfan.Qianfan;
import com.baidubce.qianfan.core.auth.Auth;
import com.baidubce.qianfan.model.chat.ChatResponse;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.suzus.aiService.common.utils.*;
import com.suzus.aiService.pojo.dto.HospitalDTO;
import com.suzus.aiService.pojo.entity.*;
import com.suzus.aiService.pojo.vo.HospitalVO;
import com.suzus.aiService.service.AskService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.time.Duration;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

@Slf4j
@Service
public class AskServiceImpl implements AskService {
    @Autowired
    private RedisTemplate redisTemplate;
    @Value("${qianfan.ak}")
    private String ak;
    @Value("${qianfan.sk}")
    private String sk;
    @Autowired
    SearchWords searchWords;
    @Autowired
    BiaoGeApi biaoGeApi;
    @Autowired
    WenZiApi wenZiApi;
    @Autowired
    HospitalCSV csv;
    //定期存储数据加快访问速度
    private void saveMedicine(Medicine medicine) {
        Duration ttl = Duration.ofDays(7);
        redisTemplate.opsForValue().set(medicine.getName(), medicine, ttl);
    }

    private Medicine getMedicine(String name) {
        return (Medicine) redisTemplate.opsForValue().get(name);
    }

    /**
     * 查询药品服务
     * 使用百度千帆大模型
     * @param questions
     * @return
     */
    public List<String> askMedicine(String questions) {
        String myQuestion = "请分点简要说明" + questions + "的作用，不良反应，饮食建议，禁忌，药物相似药。要求每项尽量简短，并且你回答的数据必须" +
                "以作用: 、不良反应: 、饮食建议: 、禁忌: 、药物相似药: 为开头。注意答案里的冒号应该为英文\": \" ，且冒号后需要加一个空格";
        ChatResponse response = new Qianfan(Auth.TYPE_OAUTH,ak,sk)
                .chatCompletion()
                .model("ERNIE-Bot") // 使用model指定预置模型
                // .endpoint("completions_pro") // 也可以使用endpoint指定任意模型 (二选一)
                .addMessage("user", myQuestion) // 添加用户消息 (此方法可以调用多次，以实现多轮对话的消息传递)
                .temperature(0.7) // 自定义超参数
                .execute(); // 发起请求
        String ans = response.getResult().replace("\n", "").replace("*","");
        log.info(ans);
        List<String> medicineInfoList = parseMedicineInfo(ans);
        return medicineInfoList;
    }
    /**
     * 查询症状服务
     * 使用百度千帆大模型
     * @param questions
     * @return
     */
    public String askSheet(String questions) {
        String myQuestion = questions+"以上是我的化验单，请你帮我简要总结一下异常情况与重点问题。要求每项尽量简短，并且你回答的数据必须" +
                "以重点问题: 、异常情况: 、健康建议: 为开头。注意答案里的冒号应该为英文\": \" ，且冒号后需要加一个空格";
        ChatResponse response = new Qianfan(Auth.TYPE_OAUTH,ak,sk)
                .chatCompletion()
                .model("ERNIE-Bot") // 使用model指定预置模型
                // .endpoint("completions_pro") // 也可以使用endpoint指定任意模型 (二选一)
                .addMessage("user", myQuestion) // 添加用户消息 (此方法可以调用多次，以实现多轮对话的消息传递)
                .temperature(0.7) // 自定义超参数
                .execute(); // 发起请求
        String answer = response.getResult().
                replace("*","");
        //int startIndex = ans.indexOf("异常情况: ");
       // String summary = ans.substring(startIndex);
        //return summary;
        return answer;
    }

    public List<String> askSheetWithThreeParts(String questions) {
        String myQuestion = questions+"以上是我的化验单，请你帮我简要总结一下异常情况与重点问题。要求每项尽量简短，并且你回答的数据必须" +
                "以重点问题: 、异常情况: 、健康建议: 为开头。注意答案里的冒号应该为英文\": \" ，且冒号后需要加一个空格";
        ChatResponse response = new Qianfan(Auth.TYPE_OAUTH,ak,sk)
                .chatCompletion()
                .model("ERNIE-Bot") // 使用model指定预置模型
                // .endpoint("completions_pro") // 也可以使用endpoint指定任意模型 (二选一)
                .addMessage("user", myQuestion) // 添加用户消息 (此方法可以调用多次，以实现多轮对话的消息传递)
                .temperature(0.7) // 自定义超参数
                .execute(); // 发起请求

        String ans = response.getResult().replace("\n", "").replace("*","");
        log.info(ans);
        List<String> analysisInfoList = parseAnalysisInfo(ans);
        return analysisInfoList;
    }

    /**
     * 查询医院服务
     * 使用百度千帆大模型
     * @param questions
     * @return
     */
    public String askHospital(String questions) {
        String myQuestion = questions+"以上是医院的信息，介绍以上医院的地址和相关的信息";
        ChatResponse response = new Qianfan(Auth.TYPE_OAUTH,ak,sk)
                .chatCompletion()
                .model("ERNIE-Bot") // 使用model指定预置模型
                // .endpoint("completions_pro") // 也可以使用endpoint指定任意模型 (二选一)
                .addMessage("user", myQuestion) // 添加用户消息 (此方法可以调用多次，以实现多轮对话的消息传递)
                .temperature(0.7) // 自定义超参数
                .execute(); // 发起请求
        String ans = response.getResult().
                replace("*","");
        //int startIndex = ans.indexOf("异常情况: ");
        // String summary = ans.substring(startIndex);
        //return summary;
        return ans;
    }

    /**
     * 传入药品图片 调用文字Api拿到药品名字
     * 然后查redis或者调大模型接口 拿到药品信息并返回
     * @param path
     * @return
     * @throws Exception
     */
    @Override
    public Medicine getMedicineByPicture(byte[] path) throws Exception {
        String json = wenZiApi.WenZiApi(path);//调用api
        ObjectMapper objectMapper = new ObjectMapper();
        JsonResponse response = objectMapper.readValue(json, JsonResponse.class);
        StringBuilder res = new StringBuilder();

        for (JsonResponse.Result.Line line : response.getResult().getLines()) {
            String text = line.getText();
            String eachRes = searchWords.search(text);
            if (res.isEmpty()) {
                log.info("比对结果为{}", eachRes);
                res.append(eachRes);
            }
        }
        log.info("药品为{}", res);
        Medicine medicine = getMedicine(String.valueOf(res));
        if (medicine == null) {
            medicine = Medicine.builder().name(res.toString()).intro(askMedicine(res.toString())).build();
            saveMedicine(medicine);
            log.info("redis存入了新数据");
        } else {
            log.info("redis中存在数据，直接返回");
        }
        return medicine;
    }

    /**
     * 调用BiaoGeApi拿到病历单信息
     * 然后调大模型接口并返回
     * @param path
     * @return
     * @throws Exception
     */
    @Override
    public Case getSymptomBySheet(byte[] path) throws Exception {
        String res=biaoGeApi.BiaoGeApi(path);
        return Case.builder().desc(askSheet(res)).build();
    }

    @Override
    public List<HospitalVO> getHospitalByCityAndDept(HospitalDTO hospitalDTO) {
        Hospital hospital = new Hospital();
        BeanUtils.copyProperties(hospitalDTO, hospital);
        List<Hospital> hospitals = csv.HospCSV(hospital);
        List<HospitalVO>hospitalVOS = hospitals.stream().
                map(setMeal -> {
                    HospitalVO v=new HospitalVO();
                    BeanUtils.copyProperties(setMeal,v);
                    return v;
                }).
                collect(Collectors.toList());
        return hospitalVOS;
    }

    /**
     * 通过图片拿到医院名字
     * 然后调大模型接口拿到医院信息
     * @param path
     * @return
     * @throws Exception
     */
    @Override
    public HospitalDesc getHospitalByPicture(byte[] path) throws Exception {
        String json= wenZiApi.WenZiApi(path);
        ObjectMapper objectMapper = new ObjectMapper();
        JsonResponse response = objectMapper.readValue(json, JsonResponse.class);
        StringBuilder result= new StringBuilder();
        for (JsonResponse.Result.Line line : response.getResult().getLines()) {
             result.append(line.getText());
        }
        return HospitalDesc.builder().desc(askHospital(result.toString())).build();
    }

    /**
     * 通过药品名拿到药品信息
     * 从redis或者大模型得到数据
     * @param name
     * @return
     * @throws JsonProcessingException
     */
    @Override
    public Medicine getMedicineByName(String name) throws JsonProcessingException {
        log.info("药品名称为{}", name);
        String res = searchWords.search(name);
        log.info("比对结果为{}", res);
        Medicine medicine = getMedicine(String.valueOf(res));
        if (medicine == null) {
            medicine = Medicine.builder().name(res).intro(askMedicine(res)).build();
            saveMedicine(medicine);
            log.info("redis存入了新数据");
        } else {
            log.info("redis中存在数据，直接返回");
        }
        return medicine;
    }

    @Override
    public Message getAdviceBySymptom(String symptom) {
        log.info("用户问题为{}", symptom);
        ChatResponse response = new Qianfan(Auth.TYPE_OAUTH,ak,sk)
                .chatCompletion()
                .model("ERNIE-Bot") // 使用model指定预置模型
                // .endpoint("completions_pro") // 也可以使用endpoint指定任意模型 (二选一)
                .addMessage("user", symptom) // 添加用户消息 (此方法可以调用多次，以实现多轮对话的消息传递)
                .temperature(0.7) // 自定义超参数
                .execute(); // 发起请求
        String ans = response.getResult().replace("\n", "").replace("*","");
        log.info(ans);
        return Message.builder().message(ans).build();
    }

    /**
     * 切割大模型返回的药品信息，得到需要的格式
     * @param info
     * @return
     */
    public List<String> parseMedicineInfo(String info) {
        List<String> medicineInfoList = new ArrayList<>();
        // 分割出“的作用: ”之后的内容和“不良反应: ”之前的内容
        String[] sections1 = info.split("作用: ");
        String part1 = sections1[1].split("不良反应: ")[0];
        medicineInfoList.add(part1.trim());
        // 分割出“不良反应: ”之后的内容和“饮食建议: ”之前的内容
        String[] sections2 = info.split("不良反应: ");
        String part2 = sections2[1].split("饮食建议: ")[0];
        medicineInfoList.add(part2.trim());
        // 分割出“不良反应: ”之后的内容和“禁忌: ”之前的内容
        String[] sections3 = info.split("饮食建议: ");
        String part3 = sections3[1].split("禁忌: ")[0];
        medicineInfoList.add(part3.trim());
        // 分割出“禁忌: ”之后的内容和“药物相似药: ”之前的内容
        String[] sections4 = info.split("禁忌: ");
        String part4 = sections4[1].split("药物相似药: ")[0];
        medicineInfoList.add(part4.trim());
        // 分割出“药物相似药: ”之后的内容
        String[] sections5 = info.split("药物相似药: ");
        medicineInfoList.add(sections5[1].trim());
        return medicineInfoList;
    }

    public List<String> parseAnalysisInfo(String info) {
        List<String> analysisInfoList = new ArrayList<>();
        // 分割出“的作用: ”之后的内容和“不良反应: ”之前的内容
        String[] sections1 = info.split("重点问题: ");
        String part1 = sections1[1].split("异常情况: ")[0];
        analysisInfoList.add(part1.trim());
        // 分割出“不良反应: ”之后的内容和“饮食建议: ”之前的内容
        String[] sections2 = info.split("异常情况: ");
        String part2 = sections2[1].split("健康建议: ")[0];
        analysisInfoList.add(part2.trim());
        // 分割出“药物相似药: ”之后的内容
        String[] sections5 = info.split("健康建议: ");
        analysisInfoList.add(sections5[1].trim());
        return analysisInfoList;
    }

}
